package org.asiainfo.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.update.UpdateChain;
import org.asiainfo.common.core.constant.CacheNames;
import org.asiainfo.common.core.constant.TenantConstants;
import org.asiainfo.common.core.constant.UserConstants;
import org.asiainfo.common.core.exception.ServiceException;
import org.asiainfo.common.core.exception.user.UserException;
import org.asiainfo.common.core.utils.SpringUtils;
import org.asiainfo.common.core.utils.StreamUtils;
import org.asiainfo.common.core.utils.StringUtils;
import org.asiainfo.common.mybatis.stream.core.page.TableDataInfo;
import org.asiainfo.common.mybatis.stream.core.page.PageQuery;
import org.asiainfo.common.mybatis.stream.utils.DataScopeUtils;
import org.asiainfo.common.satoken.utils.LoginHelper;
import org.asiainfo.common.tenant.exception.TenantException;
import org.asiainfo.common.tenant.utils.TenantHelper;
import org.asiainfo.system.api.model.LoginUser;
import org.asiainfo.system.api.model.RoleDTO;
import org.asiainfo.system.domain.*;
import org.asiainfo.system.domain.bo.SysPostBo;
import org.asiainfo.system.domain.bo.SysRoleBo;
import org.asiainfo.system.domain.table.*;
import org.asiainfo.system.domain.vo.SysRoleVo;
import org.asiainfo.system.domain.vo.SysTenantVo;
import org.asiainfo.system.domain.vo.SysUserInfoVo;
import org.asiainfo.system.domain.vo.SysUserVo;
import org.asiainfo.system.domain.bo.SysUserBo;
import org.asiainfo.system.mapper.*;
import org.asiainfo.system.service.*;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.asiainfo.common.core.utils.MapstructUtils;
import lombok.extern.slf4j.Slf4j;
import cn.hutool.core.util.ObjectUtil;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.mybatisflex.core.paginate.Page;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;
import java.util.Collection;
import java.util.List;

/**
 * 用户信息表(SysUser)表服务实现类
 *
 * @author dotor-ww
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    private final SysUserRoleMapper sysUserRoleMapper;

    private final SysPostMapper sysPostMapper;

    private final SysUserPostMapper sysUserPostMapper;

    private final SysDeptMapper sysDeptMapper;

    /**
     * 构建查询条件
     *
     * @param sysUserBo 用户信息
     * @return 构建条件
     */
    private QueryWrapper buildQueryWrapper(SysUserBo sysUserBo) {
        Map<String, Object> params = sysUserBo.getParams();
        QueryWrapper query = query();
        query.between(SysUser::getCreateTime, params.get("beginTime"), params.get("endTime"),
            ObjectUtil.isNotNull(params.get("beginTime")) && ObjectUtil.isNotNull(params.get("endTime")));

        query.eq(SysUser::getUserId, sysUserBo.getUserId(), ObjectUtil.isNotNull(sysUserBo.getUserId()));
        query.like(SysUser::getUserName, sysUserBo.getUserName(), StringUtils.isNotBlank(sysUserBo.getUserName()));
        query.eq(SysUser::getUserStatus, sysUserBo.getUserStatus(), StringUtils.isNotBlank(sysUserBo.getUserStatus()));
        query.like(SysUser::getPhoneNumber, sysUserBo.getPhoneNumber(), StringUtils.isNotBlank(sysUserBo.getPhoneNumber()));

        if (ObjectUtil.isNotNull(sysUserBo.getDeptId())) {
            List<Long> ids = queryChain()
                .select(SysDeptTableDef.SYS_DEPT.DEPT_ID)
                .from(SysDept.class)
                .where(QueryMethods.findInSet(QueryMethods.number(sysUserBo.getDeptId()), SysDeptTableDef.SYS_DEPT.ANCESTORS).gt(0))
                .listAs(Long.class);
            if (CollUtil.isEmpty(ids)) {
                ids = CollUtil.newArrayList();
            }
            ids.add(sysUserBo.getDeptId());
            query.where(SysUserTableDef.SYS_USER.DEPT_ID.in(ids));
        }
        return query;
    }

    /**
     * 通过用户名查询用户信息
     *
     * @param username 用户名
     * @return 结果
     */
    @Override
    public LoginUser getUserInfo(String username) {
        // 用户信息基于租户一起查询 查询一条数据
        SysUserVo sysUserVo = queryChain()
            .select(SysUserTableDef.SYS_USER.DEFAULT_COLUMNS)
            .from(SysUser.class)
            .where(SysUserTableDef.SYS_USER.USER_NAME.eq(username))
            .oneAs(SysUserVo.class);
        // 判断用户是否存在
        if (ObjectUtil.isNull(sysUserVo)) {
            throw new UserException("user.not.exists", username);
        }
        // 最后一次登陆的租户ID
        Long useLastTenantId = sysUserVo.getUseLastTenantId();
        // 租户ID
        Long tenantId = sysUserVo.getTenantId();

        // 租户未开启且租户为NULL, 设置默认租户
        if (!TenantHelper.isEnable() && ObjectUtil.isNull(tenantId)) {
            tenantId = TenantConstants.DEFAULT_TENANT_ID;
        }
        // 校验租户时间
        SysTenantVo sysTenantVo = SpringUtils.getBean(SysTenantService.class).queryByTenantId(tenantId);

        // 校验租户失败, 抛出异常
        if (ObjectUtil.isNull(sysTenantVo)) {
            log.info("登陆租户: {}, 不存在", tenantId);
            throw new TenantException("tenant.not.exists");
        } else if (TenantConstants.DISABLE.equals(sysTenantVo.getStatus())) {
            log.info("登陆租户: {}, 被禁用", tenantId);
            throw new TenantException("tenant.not.blocked");
        } else {
            Boolean flag = SpringUtils.getBean(SysTenantService.class).checkExpireTime(tenantId);
            if (!flag) {
                log.info("登陆租户: {}, 超过有效期", tenantId);
                throw new TenantException("tenant.expired");
            }
        }
        // 如果useLastTenantId不为空 使用最后一次登陆的租户ID, 登录的租户信息
        if (ObjectUtil.isNotNull(useLastTenantId)) {
            sysUserVo.setTenantId(useLastTenantId);
        }
        return TenantHelper.dynamic(useLastTenantId, () -> buildLoginUser(sysUserVo));
    }

    /**
     * 通过用户ID查询用户信息
     *
     * @param userId   用户ID
     * @param tenantId 租户id
     * @return 结果
     */
    @Override
    public LoginUser getUserInfo(Long userId, Long tenantId) {
        SysUser sysUser = getOne(query().eq(SysUser::getUserId, userId));
        SysUserVo sysUserVo = MapstructUtils.convert(sysUser, SysUserVo.class);
        if (ObjectUtil.isNull(sysUserVo)) {
            return null;
        }
        return buildLoginUser(sysUserVo);
    }

    /**
     * 构建登录用户
     *
     * @param sysUserVo 用户信息
     * @return 登录用户
     */
    private LoginUser buildLoginUser(SysUserVo sysUserVo) {
        LoginUser loginUser = new LoginUser();
        loginUser.setTenantId(sysUserVo.getTenantId());
        loginUser.setUserId(sysUserVo.getUserId());
        loginUser.setDeptId(sysUserVo.getDeptId());
        loginUser.setUsername(sysUserVo.getUserName());
        loginUser.setNickname(sysUserVo.getNickName());
        loginUser.setPassword(sysUserVo.getPassword());
        loginUser.setUserType(sysUserVo.getUserType());
        loginUser.setMenuPermission(SpringUtils.getBean(SysPermissionService.class).getMenuPermission(sysUserVo.getUserId()));
        loginUser.setRolePermission(SpringUtils.getBean(SysPermissionService.class).getRolePermission(sysUserVo.getUserId()));
        if (ObjectUtil.isNotNull(sysUserVo.getDeptId())) {
            SysDept sysDept = sysDeptMapper.selectOneById(sysUserVo.getDeptId());
            if (ObjectUtil.isNotNull(sysDept)) {
                loginUser.setDeptName(StringUtils.isBlank(sysDept.getDeptName()) ? "" : sysDept.getDeptName());
            }
        }
        List<SysRoleVo> sysRoleVos = SpringUtils.getBean(SysRoleService.class).selectRolesByUserId(sysUserVo.getUserId());
        List<RoleDTO> roles = BeanUtil.copyToList(sysRoleVos, RoleDTO.class);
        loginUser.setRoles(roles);
        return loginUser;
    }

    /**
     * 查询
     *
     * @param sysUserBo 筛选条件
     * @return 查询结果
     */
    @Override
    public List<SysUserVo> queryList(SysUserBo sysUserBo) {
        QueryWrapper query = DataScopeUtils.addCondition(buildQueryWrapper(sysUserBo));
        return listAs(query, SysUserVo.class);
    }

    /**
     * 通过userId查询单条数据
     *
     * @param userId 主键
     * @return 实例对象
     */
    @Override
    public SysUserVo queryById(Long userId) {
        return getOneAs(query().eq(SysUser::getUserId, userId), SysUserVo.class);
    }

    /**
     * 分页查询
     *
     * @param sysUserBo 筛选条件
     * @param pageQuery 分页对象
     * @return 查询结果
     */
    @Override
    public TableDataInfo<SysUserVo> queryPageList(SysUserBo sysUserBo, PageQuery pageQuery) {
        QueryWrapper query = DataScopeUtils.addCondition(buildQueryWrapper(sysUserBo));
        String orderInfo = pageQuery.getOrderBy();
        query.orderBy(SysUser::getUserId);
        if (StringUtils.isNotBlank(orderInfo)) {
            query.orderBy(orderInfo);
        }
        Page<SysUserVo> page = pageAs(pageQuery.buildPage(), query, SysUserVo.class);
        return TableDataInfo.build(page);
    }

    /**
     * 新增数据
     *
     * @param sysUserBo 实例对象
     * @return 实例对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(SysUserBo sysUserBo) {
        SysUser sysUser = MapstructUtils.convert(sysUserBo, SysUser.class);
        if (ObjectUtil.isNull(sysUser)) {
            return Boolean.FALSE;
        }
        boolean status = save(sysUser);
        sysUserBo.setUserId(sysUser.getUserId());
        insetUserPost(sysUserBo, false);
        insertUserRole(sysUserBo, false);
        return status;
    }

    /**
     * 新增用户岗位
     *
     * @param sysUserBo 用户对象
     * @param isClear   是否先清空
     */
    private void insetUserPost(SysUserBo sysUserBo, boolean isClear) {
        Long[] postIds = sysUserBo.getPostIds();
        if (ArrayUtil.isNotEmpty(postIds)) {
            if (isClear) {
                sysUserPostMapper.deleteByQuery(query().eq(SysUserPost::getUserId, sysUserBo.getUserId()));
            }
            List<SysUserPost> list = StreamUtils.toList(List.of(postIds), postId -> {
                SysUserPost upSysUserPost = new SysUserPost();
                upSysUserPost.setUserId(sysUserBo.getUserId());
                upSysUserPost.setPostId(postId);
                return upSysUserPost;
            });
            sysUserPostMapper.insertBatch(list);
        }
    }

    /**
     * 新增用户角色
     *
     * @param sysUserBo 用户信息
     * @param isClear   是否先清空
     */
    private void insertUserRole(SysUserBo sysUserBo, boolean isClear) {
        insertUserRole(sysUserBo.getUserId(), sysUserBo.getRoleIds(), isClear);
    }

    /**
     * 新增用户角色
     *
     * @param userId  用户ID
     * @param roleIds 角色组
     * @param isClear 是否先清空
     */
    private void insertUserRole(Long userId, Long[] roleIds, boolean isClear) {
        if (ArrayUtil.isNotEmpty(roleIds)) {

            List<SysRoleVo> sysRoleVos = SpringUtils.getBean(SysRoleService.class).selectRoleList(new SysRoleBo());
            if (CollUtil.isEmpty(sysRoleVos)) {
                throw new ServiceException("没有权限访问角色数据");
            }

            List<Long> roleIdList = StreamUtils.toList(sysRoleVos, SysRoleVo::getRoleId);
            if (!LoginHelper.isSuperAdmin(userId)) {
                roleIdList.remove(UserConstants.SUPER_ADMIN_ID);
            }

            List<Long> canDoRoleIdList = StreamUtils.filter(List.of(roleIds), roleIdList::contains);
            if (CollUtil.isEmpty(canDoRoleIdList)) {
                throw new ServiceException("没有权限访问角色数据");
            }

            if (isClear) {
                sysUserRoleMapper.deleteByQuery(query().eq(SysUserRole::getUserId, userId));
            }

            List<SysUserRole> list = StreamUtils.toList(canDoRoleIdList, roleId -> new SysUserRole(userId, roleId));
            sysUserRoleMapper.insertBatch(list);
        }
    }

    /**
     * 修改数据
     *
     * @param sysUserBo 实例对象
     * @return 实例对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(SysUserBo sysUserBo) {
        SysUser sysUser = MapstructUtils.convert(sysUserBo, SysUser.class);
        insertUserRole(sysUserBo, true);
        insetUserPost(sysUserBo, true);
        return updateById(sysUser);
    }

    /**
     * 通过主键删除数据
     *
     * @param userIds 主键
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> userIds) {
        for (Long userId : userIds) {
            checkUserAllowed(userId);
            checkUserDateScope(userId);
        }
        sysUserRoleMapper.deleteBatchByIds(userIds);
        sysPostMapper.deleteBatchByIds(userIds);
        return removeByIds(userIds);
    }

    /**
     * 校验用户是否允许操作
     *
     * @param userId 用户ID
     */
    @Override
    public void checkUserAllowed(Long userId) {
        if (ObjectUtil.isNotNull(userId) && LoginHelper.isSuperAdmin(userId)) {
            throw new ServiceException("不允许操作超级管理员");
        }
    }

    /**
     * 校验用户数据权限
     *
     * @param userId 用户ID
     */
    @Override
    public void checkUserDateScope(Long userId) {
        QueryWrapper query = DataScopeUtils.addCondition(query().eq(SysUser::getUserId, userId));
        if (ObjectUtil.isNull(userId) || LoginHelper.isSuperAdmin()) {
            return;
        }
        if (count(query) == 0) {
            throw new ServiceException("无权访问用户数据");
        }
    }

    /**
     * 更新用户状态
     *
     * @param sysUserBo 用户信息
     * @return 操作结果
     */
    @Override
    public Boolean updateUserStatus(SysUserBo sysUserBo) {
        return updateChain()
            .set(SysUser::getUserStatus, sysUserBo.getUserStatus())
            .eq(SysUser::getUserId, sysUserBo.getUserId())
            .update();
    }

    /**
     * 通过用户ID查询单条数据
     *
     * @param userId 用户ID
     * @return 查询结果
     */
    @Override
    public SysUserVo selectUserId(Long userId) {
        SysUserVo sysUserVo;
        if (LoginHelper.isSuperAdmin()) {
            sysUserVo = TenantHelper.ignore(() -> queryChain().select(SysUserTableDef.SYS_USER.DEFAULT_COLUMNS)
                .from(SysUser.class)
                .where(SysUserTableDef.SYS_USER.USER_ID.eq(userId))
                .oneAs(SysUserVo.class));
        } else {
            sysUserVo = queryChain().select(SysUserTableDef.SYS_USER.DEFAULT_COLUMNS)
                .from(SysUser.class)
                .where(SysUserTableDef.SYS_USER.USER_ID.eq(userId))
                .oneAs(SysUserVo.class);
        }
        if (ObjectUtil.isNull(sysUserVo)) {
            return sysUserVo;
        }
        List<SysRoleVo> sysRoleVos = SpringUtils.getBean(SysRoleService.class).selectRolesByUserId(userId);
        sysUserVo.setRoles(sysRoleVos);
        return sysUserVo;
    }

    /**
     * 通过用户ID查询用户信息
     *
     * @param userId 用户ID
     * @return 查询结果
     */
    @Override
    public SysUserInfoVo selectUserInfoByUserId(Long userId) {
        SysUserInfoVo sysUserInfoVo = new SysUserInfoVo();
        // 角色信息
        SysRoleBo roleBo = new SysRoleBo();
        roleBo.setRoleStatus(UserConstants.ROLE_NORMAL);
        List<SysRoleVo> sysRoleVos = SpringUtils.getBean(SysRoleService.class).selectRoleList(roleBo);
        sysUserInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? sysRoleVos : StreamUtils.filter(sysRoleVos, e -> !e.isSuperAdmin()));
        // 岗位信息
        SysPostBo postBo = new SysPostBo();
        postBo.setPostStatus(UserConstants.POST_NORMAL);
        sysUserInfoVo.setPosts(SpringUtils.getBean(SysPostService.class).queryList(postBo));
        if (ObjectUtil.isNotNull(userId)) {
            SysUserVo sysUserVo = selectUserId(userId);
            sysUserInfoVo.setSysUserVo(sysUserVo);
            sysUserInfoVo.setRoleIds(SpringUtils.getBean(SysRoleService.class).selectRoleListByUserId(userId));
            sysUserInfoVo.setPostIds(SpringUtils.getBean(SysPostService.class).selectPostListByUserId(userId));
        }
        return sysUserInfoVo;
    }

    /**
     * 校验用户名称是否唯一
     *
     * @param sysUserBo 用户信息
     * @return 校验结果
     */
    @Override
    public Boolean checkUserNameUnique(SysUserBo sysUserBo) {
        return exists(query()
            .eq(SysUser::getUserName, sysUserBo.getUserName())
            .ne(SysUser::getUserId, sysUserBo.getUserId(), ObjectUtil.isNotNull(sysUserBo.getUserId())));
    }

    /**
     * 校验手机号是否唯一
     *
     * @param sysUserBo 用户信息
     * @return 校验结果
     */
    @Override
    public Boolean checkPhoneUnique(SysUserBo sysUserBo) {
        return exists(query()
            .eq(SysUser::getPhoneNumber, sysUserBo.getPhoneNumber())
            .ne(SysUser::getUserId, sysUserBo.getUserId(), ObjectUtil.isNotNull(sysUserBo.getUserId())));
    }

    /**
     * 校验邮箱是否唯一
     *
     * @param sysUserBo 用户信息
     * @return 校验结果
     */
    @Override
    public Boolean checkEmailUnique(SysUserBo sysUserBo) {
        return exists(query()
            .eq(SysUser::getEmail, sysUserBo.getEmail())
            .ne(SysUser::getUserId, sysUserBo.getUserId(), ObjectUtil.isNotNull(sysUserBo.getUserId())));
    }

    /**
     * 根据用户ID批量获取用户信息
     *
     * @param userIds 用户ID组
     * @param deptId  部门ID
     * @return 查询结果
     */
    @Override
    public List<SysUserVo> selectUserByIds(Long[] userIds, Long deptId) {
        QueryWrapper query = DataScopeUtils.addCondition(query());
        query.select(SysUserTableDef.SYS_USER.USER_ID,
                SysUserTableDef.SYS_USER.USER_NAME,
                SysUserTableDef.SYS_USER.NICK_NAME
            ).eq(SysUser::getUserStatus, UserConstants.USER_NORMAL)
            .eq(SysUser::getDeptId, deptId, ObjectUtil.isNotNull(deptId))
            .in(SysUser::getUserId, userIds, ObjectUtil.isNotNull(userIds));
        return listAs(query, SysUserVo.class);
    }

    /**
     * 重置用户密码
     *
     * @param userId 用户ID
     * @param pwd    密码
     * @return 操作结果
     */
    @Override
    public Boolean resetUserPwd(Long userId, String pwd) {
        return updateChain().set(SysUser::getPassword, pwd)
            .eq(SysUser::getUserId, userId)
            .update();
    }

    /**
     * 用户授权角色
     *
     * @param userId  用户ID
     * @param roleIds 角色组
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertUserAuth(Long userId, Long[] roleIds) {
        insertUserRole(userId, roleIds, true);
    }

    /**
     * 获取部门下的所有用户信息
     *
     * @param deptId 部门ID
     * @return 结果
     */
    @Override
    public List<SysUserVo> selectUserListByDeptId(Long deptId) {
        QueryWrapper query = query().eq(SysUser::getDeptId, deptId);
        query.orderBy(SysUser::getUserId);
        return listAs(query, SysUserVo.class);
    }

    /**
     * 通过用户名查询用户昵称
     *
     * @param userId 用户ID
     * @return 用户昵称
     */
    @Cacheable(cacheNames = CacheNames.SYS_NICK_NAME, key = "#userId")
    @Override
    public String getNickNameByUserId(Long userId) {
        return queryChain().select(SysUser::getNickName)
            .from(SysUser.class)
            .where(SysUserTableDef.SYS_USER.USER_ID.eq(userId))
            .oneAs(String.class);
    }

    /**
     * 通过用户ID查询用户名
     *
     * @param userId 用户ID
     * @return 用户名
     */
    @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId")
    @Override
    public String getUserNameByUserId(Long userId) {
        return queryChain().select(SysUser::getUserName)
            .from(SysUser.class)
            .where(SysUserTableDef.SYS_USER.USER_ID.eq(userId))
            .oneAs(String.class);
    }

    /**
     * 更新最后一次登陆的租户ID
     *
     * @param tenantId 租户ID
     */
    @Override
    public void updateUseLastTenantIdByUserId(Long tenantId) {
        Long userId = LoginHelper.getUserId();
        TenantHelper.ignore(() -> UpdateChain.of(SysUser.class)
            .from(SysUser.class)
            .set(SysUser::getUseLastTenantId, tenantId)
            .where(SysUserTableDef.SYS_USER.USER_ID.eq(userId))
            .update());
    }

    /**
     * 通过用户ID列表查询用户昵称列表
     *
     * @param userIds 用户ID列表
     * @return 用户昵称列表
     */
    @Override
    public List<String> selectNickNameByUserIdList(List<Long> userIds) {
        return queryChain().select(SysUser::getNickName)
            .from(SysUser.class)
            .where(SysUserTableDef.SYS_USER.USER_ID.in(userIds))
            .listAs(String.class);
    }
}
